2

Interactivity and Dynamic UIs

React.findDOMNode()

组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM (virtual DOM)。只有当它插入文档以后,才会变成真实的 DOM 。根据 React 的设计,所有的 DOM 变动,都先在虚拟 DOM上发生,然后再将实际发生变动的部分,反映在真实 DOM上,这种算法叫做 DOM diff ,它可以极大提高网页的性能表现。但是,有时需要从组件获取真实 DOM 的节点,这时就要用到 React.findDOMNode 方法。

var MyComponent = React.createClass({
  handleClick: function() {
    React.findDOMNode(this.refs.myTextInput).focus();
  },
  render: function() {
    return (
      <div>
        <input type="text" ref="myTextInput" />
        <input type="button" value="Focus the text input" onClick={this.handleClick} />
      </div>
    );
  }
});

React.render(
  <MyComponent />,
  document.getElementById('example')
);

需要注意的是,由于 React.findDOMNode 方法获取的是真实 DOM ,所以必须等到虚拟 DOM 插入文档以后,才能使用这个方法,否则会返回 null 。上面代码中,通过为组件指定 Click 事件的回调函数,确保了只有等到真实 DOM 发生 Click 事件之后,才会调用 React.findDOMNode 方法。

getInitialState

设置State的初始状态。

var MyComponent = React.createClass({
    getInitialState: function(){
        return {
            count: 5
        }
    },
    render: function(){
        return (
            <h1>{this.state.count}</h1>
        )
    }
});

Style

参考资料

  • React行内样式最佳实践

Inline-style

在React中,如果要使用行内元素,不可以直接使用style="”这种方式,可以有:

import React from 'react';

var style = {
  backgroundColor: '#EEE'
};

export default React.createClass({
  render: function () {
    return (
      <div style={style}>
      //或者<div style={{backgroundColor: '#EEE'}}>
        <h1>Hello world</h1>
      </div>
    )
  }
});

可以看出,React的style属性接收的也是一个JavaScript对象。

Class

你可以根据这个策略为每个组件创建 CSS 文件,可以让组件名和 CSS 中的 class 使用一个命名空间,来避免一个组件中的一些 class 干扰到另外一些组件的 class。

app/components/MyComponent.css

.MyComponent-wrapper {
  background-color: #EEE;
}

app/components/MyComponent.jsx

import './MyComponent.css';
import React from 'react';

export default React.createClass({
  render: function () {
    return (
      <div className="MyComponent-wrapper">
        <h1>Hello world</h1>
      </div>
    )
  }
});
Multiple Class

上文中提及的利用className方式赋值,如果在存在多个类名的情况下:

render: function() {
  var cx = React.addons.classSet;
  var classes = cx({
    'message': true,
    'message-important': this.props.isImportant,
    'message-read': this.props.isRead
  });
  // same final string, but much cleaner
  return <div className={classes}>Great, I'll be there.</div>;
}

Event

React对于事件的支持非常完善,可以查看这里。React 实现了一个“合成事件”层(synthetic event system),这个事件模型保证了和 W3C 标准保持一致,所以不用担心有什么诡异的用法,并且这个事件层消除了 IE 与 W3C 标准实现之间的兼容问题。“合成事件”额外提供了两个好处:自动绑定上下文和事件委托

  • “合成事件”自动将事处理件方法的上下文绑到当前组件,所以 handleClick 方法里面可以直接使用this.setState

  • “合成事件”会以事件委托(event delegation)的方式绑定到组件最上层,并且在组件卸载(unmount)的时候自动销毁绑定的事件。

当然,在React中,也可以使用原生事件,比如你在 componentDidMount 方法里面通过 addEventListener 绑定的事件就是浏览器原生事件。使用原生事件的时候注意在 componentWillUnmount 解除绑定 removeEventListener。所有通过 JSX 这种方式绑定的事件都是绑定到“合成事件”,除非你有特别的理由,建议总是用 React 的方式处理事件。

Event Bind

最基本的绑定方式就是依靠类似于onClick={handleClick}的方式,要注意,这里不同于ng-click,onClick传递的参数只能是一个方法,而不能是一个调用。如果要简单来写的话可以采Lambda表达式的方式:

onClick={()=>{alert(1);}}
Event Params

给事件处理函数传递额外参数的方式:bind(this, arg1, arg2, ...)

render: function() {
    return <p onClick={this.handleClick.bind(this, 'extra param')}>;
},
handleClick: function(param, event) {
    // handle click
}

由上面可以看出,Event一般都是作为最后一个参数传递到handleClick中,这里的event是SyntheticEvent对象,它的主要属性如下:

boolean bubbles
boolean cancelable
DOMEventTarget currentTarget
boolean defaultPrevented
number eventPhase
boolean isTrusted
DOMEvent nativeEvent
void preventDefault()
void isDefaultPrevented()
void stopPropagation()
void isPropagationStopped()
DOMEventTarget target
number timeStamp
string type
List Element

在React中,也会经常遇到需要为某个群组绑定事件的情况,可以参考如下代码:

var GroceryList = React.createClass({
  handleClick: function(i) {
    console.log('You clicked: ' + this.props.items[i]);
  },
  render: function() {
    return (
      <div>
        {this.props.items.map(function(item, i) {
          return (
            <div onClick={this.handleClick.bind(this, i)} key={i}>{item}</div>
          );
        }, this)}
      </div>
    );
  }
});
React.render(
  <GroceryList items={['Apple', 'Banana', 'Cranberry']} />, mountNode
);

TouchEvent

If you'd like to use React on a touch device such as a phone or tablet, simply call React.initializeTouchEvents(true); to enable touch event handling.

接口暴露

譬如在某个子组件中,提供了某个方法:

var ButtonComponent = React.createClass({
    getDragonKillingSword: function(){
        //送宝刀
    },
    render: function(){
        return (<button onClick={this.getDragonKillingSword}>屠龙宝刀,点击就送</button>);
    }
});

如果在父组件中想手动调用该方法,则可以利用ref方式:

var ImDaddyComponent = React.createClass({
  render: function(){
    return (
      <div>
        //其他组件
        <ButtonComponent />
        //其他组件
      </div>
    );
  }
});

在父组件的功能方程中:

this.refs.getSwordButton.getDragonKillingSword();

反之,如果需要在子组件中调用父组件的方法,则可以直接将父组件的方法作为Props参数传入到子组件中:

<ButtonComponent clickCallback={this.getSwordButtonClickCallback}/>

Ajax

组件的数据来源,通常是通过 Ajax 请求从服务器获取,可以在componentDidMount 方法中设置 Ajax 请求,等到请求成功,再用 this.setState 方法重新渲染 UI。

    var UserGist = React.createClass({
      getInitialState: function() {
        return {
          username: '',
          lastGistUrl: ''
        };
      },

      componentDidMount: function() {
        $.get(this.props.source, function(result) {
          var lastGist = result[0];
          if (this.isMounted()) {
            this.setState({
              username: lastGist.owner.login,
              lastGistUrl: lastGist.html_url
            });
          }
        }.bind(this));
      },

      render: function() {
        return (
          <div>
            {this.state.username}'s last gist is
            <a href={this.state.lastGistUrl}>here</a>.
          </div>
        );
      }
    });

    React.render(
      <UserGist source="https://api.github.com/users/octocat/gists" />,
      document.body
    );

不过笔者习惯还是将整个获取数据,处理数据的业务逻辑放在Angular中进行。


王下邀月熊_Chevalier
22.5k 声望8.5k 粉丝

爱代码 爱生活 希望成为全栈整合师